/**
 * @file test_GameWindow.cpp
 * @brief Unit testing for the game window.
 *
 * Copyright 2009 Ryan Chew
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *    http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

// Delectable Headers //
#include <dbl/Core/GameWindow.h>

// Chewable Headers //
#include <cbl/Util/Stopwatch.h>
#include <cbl/Core/Game.h>

// Google Test //
#include <gtest/gtest.h>

using namespace dbl;

class GameWindowFixture : public ::testing::Test
{
public:

	GameWindowFixture()
		: dummyGame( "GameWindow_Test" ),
		gameWindow( dummyGame ),
		timeout( 0.0 ),
		windowCreated( false ),
		windowDestroyed( false ),
		mouseMoveCount( 0 )
	{
	}

	void SetUp()
	{
		timeout = 0.5;
		fullscreenTimeout = 2;

		gameWindow.OnWindowCreated		+= *this;
		gameWindow.OnWindowDestroyed	+= *this;
		gameWindow.OnWindowClosed		+= *this;

		gameWindow.OnWindowKeyChar		+= *this;
		gameWindow.OnWindowKeyDown		+= *this;
		gameWindow.OnWindowKeyUp		+= *this;

		gameWindow.OnWindowMouseWheel	+= *this;
		gameWindow.OnWindowMouseMove	+= *this;
		gameWindow.OnWindowMouseDown	+= *this;
		gameWindow.OnWindowMouseUp		+= *this;
		
		gameWindow.OnWindowMouseEnter	+= *this;
		gameWindow.OnWindowMouseLeave	+= *this;
	}

	void TearDown()
	{
		gameWindow.OnWindowCreated		-= *this;
		gameWindow.OnWindowDestroyed	-= *this;
		gameWindow.OnWindowClosed		-= *this;

		gameWindow.OnWindowKeyChar		-= *this;
		gameWindow.OnWindowKeyDown		-= *this;
		gameWindow.OnWindowKeyUp		-= *this;
		
		gameWindow.OnWindowMouseWheel	-= *this;
		gameWindow.OnWindowMouseMove	-= *this;
		gameWindow.OnWindowMouseDown	-= *this;
		gameWindow.OnWindowMouseUp		-= *this;

		gameWindow.OnWindowMouseEnter	-= *this;
		gameWindow.OnWindowMouseLeave	-= *this;
	}

	DECL_EVENT_HANDLER( E::OnWindowCreated )
	{
		Stdout << "[GameWindow] Window created.\n";
		windowCreated = true;
	}

	DECL_EVENT_HANDLER( E::OnWindowDestroyed )
	{
		Stdout << "[GameWindow] Window destroyed.\n";
		windowDestroyed = true;
	}
	
	DECL_EVENT_HANDLER( E::OnWindowClosed )
	{
		Stdout << "[GameWindow] Window closed.\n";
	}

	DECL_EVENT_HANDLER( E::OnWindowKeyChar )
	{
		Stdout << static_cast< char >( args.Unicode );
	}

	DECL_EVENT_HANDLER( E::OnWindowKeyDown )
	{
		Stdout << "[GameWindow] Key down." << std::endl;
	}

	DECL_EVENT_HANDLER( E::OnWindowKeyUp )
	{
		Stdout << "[GameWindow] Key up." << std::endl;
	}
	
	DECL_EVENT_HANDLER( E::OnWindowMouseWheel )
	{
		Stdout << "[GameWindow] Mouse wheel move: " << args.Delta << std::endl;
	}

	DECL_EVENT_HANDLER( E::OnWindowMouseMove )
	{
		++mouseMoveCount;
	}

	DECL_EVENT_HANDLER( E::OnWindowMouseDown )
	{
		Stdout << "[GameWindow] MouseDown (";
		switch( args.Button )
		{
		case Mouse::Left:
			Stdout << "Left";
			break;
		case Mouse::Right:
			Stdout << "Right";
			break;
		case Mouse::Middle:
			Stdout << "Middle";
			break;
		case Mouse::XButton1:
			Stdout << "Xbutton1";
			break;
		case Mouse::XButton2:
			Stdout << "Xbutton2";
			break;
		}
		Stdout << "): (" << args.X << ", " << args.Y << ")" << std::endl;
	}

	DECL_EVENT_HANDLER( E::OnWindowMouseUp )
	{
		Stdout << "[GameWindow] MouseUp (";
		switch( args.Button )
		{
		case Mouse::Left:
			Stdout << "Left";
			break;
		case Mouse::Right:
			Stdout << "Right";
			break;
		case Mouse::Middle:
			Stdout << "Middle";
			break;
		case Mouse::XButton1:
			Stdout << "Xbutton1";
			break;
		case Mouse::XButton2:
			Stdout << "Xbutton2";
			break;
		}
		Stdout << "): (" << args.X << ", " << args.Y << ")" << std::endl;
	}
	
	DECL_EVENT_HANDLER( E::OnWindowMouseEnter )
	{
		Stdout << "[GameWindow] Mouse entered." << std::endl;
	}

	DECL_EVENT_HANDLER( E::OnWindowMouseLeave )
	{
		Stdout << "[GameWindow] Mouse left." << std::endl;
	}

protected:

	cbl::Game			dummyGame;
	cbl::Stopwatch		stopwatch;
	cbl::Stopwatch		lapwatch;
	GameWindow				gameWindow;
	cbl::Float64		timeout;
	cbl::Float64		fullscreenTimeout;

	bool					windowCreated;
	bool					windowDestroyed;
	cbl::Uint32		mouseMoveCount;
};

TEST_F( GameWindowFixture, GameWindow_CreateDestroy )
{
	ASSERT_FALSE( windowCreated );
	gameWindow.Initialise();
	gameWindow.SetTitle( "HEYHEYHEY!" );
	ASSERT_TRUE( windowCreated );

	stopwatch.Start();
	while( stopwatch.GetElapsedTime().TotalSeconds() < timeout )
	{
		gameWindow.Update( cbl::GameTime() );
	}
	stopwatch.Stop();
	
	Stdout << "[GameWindow] Mouse move events: " << mouseMoveCount << std::endl;

	ASSERT_FALSE( windowDestroyed );
	gameWindow.Shutdown();
	ASSERT_TRUE( windowDestroyed );
}

TEST_F( GameWindowFixture, GameWindow_Position )
{
	ASSERT_FALSE( windowCreated );
	gameWindow.Initialise();
	ASSERT_TRUE( windowCreated );

	stopwatch.Start();
	lapwatch.Start();
	cbl::Uint32 x = 0;
	while( stopwatch.GetElapsedTime().TotalSeconds() < timeout )
	{
		gameWindow.Update( cbl::GameTime() );

		if( lapwatch.GetElapsedTime().TotalSeconds() > ( timeout / 10 ) )
		{
			x += 20;
			gameWindow.SetPosition( x, x );
			lapwatch.Stop();
			lapwatch.Reset();
			lapwatch.Start();
		}
	}
	lapwatch.Stop();
	stopwatch.Stop();
	
	Stdout << "[GameWindow] Mouse move events: " << mouseMoveCount << std::endl;

	ASSERT_FALSE( windowDestroyed );
	gameWindow.Shutdown();
	ASSERT_TRUE( windowDestroyed );
}

TEST_F( GameWindowFixture, GameWindow_Size )
{
	ASSERT_FALSE( windowCreated );
	gameWindow.Initialise();
	ASSERT_TRUE( windowCreated );

	stopwatch.Start();
	lapwatch.Start();
	cbl::Uint32 width = 1024;
	cbl::Uint32 height = 768;
	while( stopwatch.GetElapsedTime().TotalSeconds() < timeout )
	{
		gameWindow.Update( cbl::GameTime() );

		if( lapwatch.GetElapsedTime().TotalSeconds() > ( timeout / 10 ) )
		{
			width -= 50;
			height -= 50;
			gameWindow.SetSize( GameWindowSize( width, height ) );
			lapwatch.Stop();
			lapwatch.Reset();
			lapwatch.Start();
		}
	}
	lapwatch.Stop();
	stopwatch.Stop();
	
	Stdout << "[GameWindow] Mouse move events: " << mouseMoveCount << std::endl;

	ASSERT_FALSE( windowDestroyed );
	gameWindow.Shutdown();
	ASSERT_TRUE( windowDestroyed );
}

TEST_F( GameWindowFixture, GameWindow_Fullscreen )
{
	ASSERT_FALSE( windowCreated );
	gameWindow.Initialise();
	ASSERT_TRUE( windowCreated );

	stopwatch.Start();

	gameWindow.SetFullscreen( true );
	while( stopwatch.GetElapsedTime().TotalSeconds() < fullscreenTimeout )
	{
		gameWindow.Update( cbl::GameTime() );
	}

	stopwatch.Stop();
	stopwatch.Reset();
	stopwatch.Start();

	gameWindow.SetFullscreen( false );
	while( stopwatch.GetElapsedTime().TotalSeconds() < fullscreenTimeout )
	{
		gameWindow.Update( cbl::GameTime() );
	}

	stopwatch.Stop();
	
	Stdout << "[GameWindow] Mouse move events: " << mouseMoveCount << std::endl;

	ASSERT_FALSE( windowDestroyed );
	gameWindow.Shutdown();
	ASSERT_TRUE( windowDestroyed );
}